package zzu.xjc.http;

import zzu.xjc.http.util.Logger;

import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.Iterator;
import java.util.List;

public class HttpProtocolParser {
    private static final int TempBufferSize = 1024;

    static HttpRequest parse(List<ByteBuffer> bufferList){

        ByteBuffer tmpBuffer = ByteBuffer.allocate(TempBufferSize);
        HttpRequest request = new HttpRequest();
        /* Position the range of Http header 定位Http头部范围 */
        byte b , b1 , b2 , b3 ;
        ByteBuffer buffer = bufferList.get(0);
        while (buffer.hasRemaining()){
            // \r\n\r\n is the separator between http header and http body
            b = buffer.get();
            if (b == '\r'){
                b1 = buffer.get();
                b2 = buffer.get();
                b3 = buffer.get();
                if (b1 == '\n' && b2 == '\r' && b3 == '\n'){
                    break;
                }
                tmpBuffer.put(b).put(b1).put(b2).put(b3);
                continue;
            }
            tmpBuffer.put(b);
        }
        /* Handle Http header 处理Http头部 */
        CharBuffer charBuffer = StandardCharsets.US_ASCII.decode(tmpBuffer.flip());
        String httpHeader = charBuffer.toString();
        /*Logger.info("========Header========");
        Logger.info(httpHeader);
        Logger.info("======================");*/
        String[] headerLines = httpHeader.split("\\r\\n");
        // 处理Http的第一行   Process the first line of Http
        // 示例/Example     GET /index.html?username=xjc&id=666 HTTP/1.1
        String[] firstLine = headerLines[0].split(" ");
        request.setMethod(Http.Method.fromString(firstLine[0]));

        if(firstLine.length <= 1){
            Logger.info( "Error First Line of Http Request : " + headerLines[0]);
            return null;
        }
        String url = firstLine[1];
        if (url.contains("?")){
            // url carrying parameters  url携带参数
            Charset charset = getCharset(url);
            url = URLDecoder.decode(url,charset);
            int index = url.indexOf("?");
            request.setUrl(Http.Url.process(url.substring(0,index)));
            request.initParams();
            String[] paramKVs = url.substring(index + 1).split("&");
            // KV : Key-Value
            for (int i = 0; i < paramKVs.length; i++) {
                String[] singleKV = paramKVs[i].split("=");
                if (singleKV.length < 2){
                    continue;
                }
                request.putParam(singleKV[0],singleKV[1]);
            }
        }else{
            request.setUrl(Http.Url.process(url));
        }
        // 处理Http的剩余若干行  Process the remaining lines of Http
        // 示例/Example:      User-Agent: PostmanRuntime/7.26.8
        for (int i = 1; i < headerLines.length; i++) {
            String[] lineKV = headerLines[i].split(": ");
            if (lineKV.length < 2){
                continue;
            }
            request.putHeader(lineKV[0],lineKV[1].trim());
        }
        // 处理可能存在的Cookie Processing cookies that may exist
        // 示例/Example:      Cookie: SessionID=93248D9AF1CB36FEC3D30EE3A5367193;UserID=114514
        String cookie = request.getHeader("Cookie");
        if (cookie != null){
            request.initCookies();
            String[] cookieKVs = cookie.split(";");
            for (int i = 0; i < cookieKVs.length; i++) {
                String[] kv = cookieKVs[i].split("=");
                if (kv.length < 2){
                    continue;
                }
                request.putCookie(kv[0],kv[1]);
            }
        }

        if (request.method() == Http.Method.POST || request.method() == Http.Method.PUT){
            /* Handle Http body  处理Http体 */
            String contentType = request.getHeader("Content-Type");
            String charsetStr = "UTF-8";
            if (contentType.contains(";")){
                // Content-Type此时可能含有Charset字段 Content-Type may contain a charset field
                // 示例/Example:   Content-Type: application/json;Charset=UTF-8
                String[] contentTypeKVs = contentType.split(";");
                for (int i = 0; i < contentTypeKVs.length; i++) {
                    String[] ctKV = contentTypeKVs[i].split("=");
                    if (ctKV.length < 2){
                        continue;
                    }
                    if (ctKV[0].contains("Charset")){
                        charsetStr = ctKV[1].trim();
                    }
                }
            }
            Charset charset = Charset.forName(charsetStr);

            Iterator<ByteBuffer> iter = bufferList.iterator();
            ByteBuffer firstBuffer = iter.next();
            StringBuilder body = new StringBuilder();
            body.append(charset.decode(firstBuffer));
            while (iter.hasNext()){
                body.append(charset.decode(iter.next()));
            }
            request.setBody(body.toString());
        }
        return request;
    }

    static Charset getCharset(String str){
        if (str.equals(new String(str.getBytes(StandardCharsets.UTF_8),StandardCharsets.UTF_8))){
            return StandardCharsets.UTF_8;
        }
        String gbk = "GBK";
        try {
            if (str.equals(new String(str.getBytes(gbk), gbk))){
                return Charset.forName(gbk);
            }
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
        String gb2312 = "GB2312";
        try {
            if (str.equals(new String(str.getBytes(gb2312), gb2312))){
                return Charset.forName(gb2312);
            }
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
        if (str.equals(new String(str.getBytes(StandardCharsets.ISO_8859_1),StandardCharsets.ISO_8859_1))){
            return StandardCharsets.ISO_8859_1;
        }
        if (str.equals(new String(str.getBytes(StandardCharsets.US_ASCII),StandardCharsets.US_ASCII))){
            return StandardCharsets.US_ASCII;
        }
        // Latin-1
        if (str.equals(new String(str.getBytes(StandardCharsets.UTF_16),StandardCharsets.UTF_16))){
            return StandardCharsets.UTF_16;
        }
        return StandardCharsets.UTF_8;
    }
}
